/**
 * 家主直系，用红实线，配偶子女家主嫡生 用蓝实线，继子用蓝虚线，养子用白虚线。嫡子用黄实线
 */
package com.findu.view;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

import com.example.findu.R;
import com.findu.model.ElementRect;
import com.findu.model.FUFamilyMember;
import com.findu.model.FUFamilyTree;
import com.findu.model.FUMateTree;

/**
 *
 * @author 夏启根
 */
public class AncestryView extends View implements GestureDetector.OnGestureListener {

    private Paint oPaint = null;
    private Rect[] div = {null, null, null, null};  //  最多只画4层
    private String buf = "";
    private FUFamilyTree data = null;
    private GestureDetector gestureDetector = null;
    private int fontSize = 16;
    private int rowledge = 2;
    private final int backgroupColor = 0xffffffff;
    private final int colorRed = 0xffff0000;
    private final int colorBlue = 0xff0000ff;
    private final int colorYellow = 0xffff00ff;
    private final int colorWhite = 0xffffffff;
    private final int selectedBgColor = 0xff888888;
    private final int contentColor = 0xff000000;
//    private int titleColor = 0xff000000;
//    private int titleBgColor = 0xffaaaaaa;
//    private int gridColor = 0xff888888;
//    private int selectedColor = 0xffffff22;
    private int vSpace = 48;     //  垂直间隔
    private int hSpace = 24;     //  水平间隔
    private int head = 8;
	private int lineWidth = 2;
    private int itemHeight = 40;
    private int itemWidth = 60;
    private double zoomFactor = zoomDefault;
    private Handler handler = null;
    private int level, childCounts[];
    private Bitmap mImg, wImg;
    private DisplayMetrics metric;
    private ArrayList<ElementRect> elems;
    private OnFamilyMemberClickedListener click;
    
    private boolean zoomMode = false;
    private double distanceBetweenFingers = 0;
    private static final double zoomDefault = 0.8f;
    private static final double zoomSpec = 0.1;
    private static final double zoomMax = 10;
    private static final double zoomMin = 0.1;

    public AncestryView(Context context) {
        super(context);
        initPaint(context);
    }

    public AncestryView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint(context);
    }

    public AncestryView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initPaint(context);
    }

    private void initPaint(Context context) {
        metric = new DisplayMetrics();
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(metric);
        this.fontSize = (int) (fontSize * metric.density + 0.5);
        this.rowledge = (int) (rowledge * metric.density + 0.5);
        this.hSpace = (int) (this.hSpace * metric.density + 0.5);
        this.vSpace = (int) (this.vSpace * metric.density + 0.5);
        this.lineWidth = (int) (this.lineWidth * metric.density + 0.5);
        this.head = (int) (this.head * metric.density + 0.5);
        oPaint = new Paint();
        oPaint.setTextSize(getFontSize());
        handler = new Handler();
        mImg = BitmapFactory.decodeResource(context.getResources(), R.drawable.man);
        wImg = BitmapFactory.decodeResource(context.getResources(), R.drawable.woman);
        this.itemWidth = mImg.getWidth();
        this.itemHeight = (int) (mImg.getHeight() * 1.5 + 0.5);
//        setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
        elems = new ArrayList<ElementRect>();
        gestureDetector = new GestureDetector(context, this);
        for (int i = 0; i < 4; i++) {
            div[i] = new Rect();
        }
    }

    public void setOnFamilyMemberClickedListener(OnFamilyMemberClickedListener listener) {
        this.click = listener;
    }

    public void setData(FUFamilyTree d) {
//        if(data!=null) data.clear();
        data = d;
        if (d == null) {
            return;
        }
        this.level = data.getLevel(0);
        this.childCounts = new int[this.level];
        data.getChilds(0, childCounts);
        updateDataView();

    }

    public void updateDataView() {
        handler.postDelayed(new Runnable() {        //  更新界面

            @Override
            public void run() {
                updateListView();
            }
        }, 200);
    }

    private void updateListView() {
        this.invalidate();

    }

    private void drawFamilyMember(Canvas canvas, Rect rect, FUFamilyMember member) {
        Rect r = new Rect();
        rect.right = rect.left + getItemWidth();
        rect.bottom = (int) (rect.top + getImageHeight());
        if (member == null) {
            oPaint.setColor(this.selectedBgColor);
            rect.bottom = rect.top + getItemHeight();
            canvas.drawRect(rect, oPaint);
        } else {
            ElementRect elem = new ElementRect();
            elem.id = member.id;
            elem.top = rect.top;
            elem.left = rect.left;
            elem.right = rect.left + getItemWidth();
            elem.bottom = rect.top + getItemHeight();
            elems.add(elem);
            if (member.sex == FUFamilyMember.SEX_Man) {
                canvas.drawBitmap(mImg, null, rect, null);
            } else {
                canvas.drawBitmap(wImg, null, rect, null);
            }

            oPaint.setColor(this.backgroupColor);
            r.top = rect.bottom;
            r.bottom = r.top + (int) (getImageHeight() / 2);
            r.left = rect.left;
            r.right = rect.right;
            canvas.drawRect(r, oPaint);
            oPaint.setColor(this.contentColor);
            int posY = r.top + getHead() - (int) oPaint.ascent();;

            if (member.lastname == null || "null".equals(member.lastname) || member.lastname.length() == 0) {
                buf = member.name;
            } else {
                buf = member.name + "(" + member.lastname + ")";         //  名
            }
            int cc = (int) oPaint.measureText(buf);
            int posX = (getItemWidth() - cc) / 2;
            canvas.drawText(this.buf, 0, buf.length(), r.left + posX, posY, oPaint);
            cc = (int) oPaint.measureText(member.surname);
            posY += getFontSize() + this.rowledge;
            posX = (getItemWidth() - cc) / 2;
            canvas.drawText(member.surname, 0, member.surname.length(), r.left + posX, posY, oPaint);
        }
        rect.left += getItemWidth() + gethSpace();
    }

    private void drawFamily(Canvas canvas, FUFamilyTree family, int lvl) {
        Rect r = new Rect();
        Rect rect = div[lvl];
        if (family == null) {
            return;
        } else {
            //  画家主
            //----------  先画关系
            if (family.parent != null) {
                //  画父母关系
                r.top = rect.top - getvSpace() / 2;
                r.left = rect.left + getItemWidth() / 2;
                r.right = r.left + this.lineWidth;
                r.bottom = rect.top;
                oPaint.setColor(this.colorYellow);
                canvas.drawRect(r, oPaint);
                r.right = family.parent.x;
                r.bottom = r.top + this.lineWidth;
                int x;
                if(r.right < r.left){
                	x = r.left;
                	r.left = r.right;
                	r.right = x;
                }
                canvas.drawRect(r, oPaint);
            }
            drawFamilyMember(canvas, rect, family.homeOwner);
            if (family.isHaveChild() || family.isHaveMate()) {
                r.left = rect.right;
                r.top = rect.top + getItemHeight() / 2;
                r.right = r.left + gethSpace() / 2;
                r.bottom = r.top + this.lineWidth;
                oPaint.setColor(this.colorRed);
                canvas.drawRect(r, oPaint);
                if (family.isHaveChild()) {    //  有子女
                    r.left = rect.right + gethSpace() / 2;
                    r.right = r.left + this.lineWidth;
                    r.bottom = rect.top + getItemHeight() + getvSpace() / 2;
                    oPaint.setColor(this.colorYellow);
                    canvas.drawRect(r, oPaint);
                    family.x = r.left;
                }
            }
            // 画配偶
            if (family.isHaveMate()) {
                for (FUMateTree mate : family.mates) {
                    r.left = rect.left - gethSpace();
                    r.top = rect.top + getItemHeight() / 2;
                    r.right = rect.left;
                    r.bottom = r.top - this.lineWidth;
                    oPaint.setColor(this.colorBlue);
                    canvas.drawRect(r, oPaint);
                    drawFamilyMember(canvas, rect, mate);
                }
            }
            // 画子女
            if (family.childrens != null) {
                for (FUFamilyTree sub : family.childrens) {
                    drawFamily(canvas, sub, lvl + 1);
                }
            }
            if (family.isHaveMate()) {
                for (FUMateTree mate : family.mates) {
                    if (mate.childrens != null) {
                        for (FUFamilyTree sub : mate.childrens) {
                            drawFamily(canvas, sub, lvl + 1);
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {

        this.elems.clear();
        if (data != null) {
            // 计算各代的起始位置
            for (int i = 0; i < level; i++) {
                div[i].left = getPaddingLeft() + (this.getWidth() - getPaddingLeft() - getPaddingRight() - getItemWidth() * this.childCounts[i] - gethSpace() * (this.childCounts[i] - 1)) / 2;
                div[i].top = getPaddingTop() + getvSpace() + (getItemHeight() + getvSpace()) * i;
            }
            drawFamily(canvas, data, 0);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
        oPaint.setTextSize(getFontSize());
        Log.i("test", "calculate measure");
    }

    private int measureWidth(int measureSpec) {
        int result = 0;
        for (int i = 0; i < this.childCounts.length; i++) {
            if (result < this.childCounts[i]) {
                result = this.childCounts[i];
            }
        }
        result = (getItemWidth() + gethSpace()) * result + gethSpace();
        return result;
    }

    private int measureHeight(int measureSpec) {
        int result = 0;
        result = this.level * (getItemHeight() + getvSpace()) + getvSpace();
        return result;
    }

    public boolean onDown(MotionEvent arg0) {
        return true;
    }

    public void onShowPress(MotionEvent arg0) {
    }

    /**
     *
     * @param evt
     * @param evt
     * @return
     */
    public boolean onSingleTapUp(MotionEvent evt) {
        int pointY = (int) evt.getY();
        int pointX = (int) evt.getX();
        for (ElementRect r : elems) {
            if (pointX > r.left && pointX < r.right && pointY > r.top && pointY < r.bottom) {
                if (click != null) {
                    click.onClick(r.id);
                }
            }
        }
        return true;
    }

    public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
        return true;
    }

    public void onLongPress(MotionEvent arg0) {
    }

    public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    	if(event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
    			&& event.getPointerCount() == 2){
    		zoomMode = true;
    		distanceBetweenFingers = calDistanceBetweenFingers(event);
    		Log.i("test", "set zoom mode");
    		return true;
    	}else if(zoomMode){
    		switch(event.getActionMasked()){
    		case MotionEvent.ACTION_POINTER_UP:
    			if(event.getPointerCount() >= 2)
    				break;
    		case MotionEvent.ACTION_CANCEL:
    		case MotionEvent.ACTION_UP:
    			zoomMode = false;
    			Log.i("test", "release zoom");
    			break;
    		case MotionEvent.ACTION_MOVE:
    			if(zoomMode && event.getPointerCount() >= 2)
	    		{
	    			double distance = calDistanceBetweenFingers(event);
	    			double factor = (distance / distanceBetweenFingers - 1) * 10;
	    			Log.i("test", String.format("old %f, new %f, factor %f", distanceBetweenFingers, distance, factor));
	    			if(Math.abs(factor) > zoomSpec){
	    				double zf = zoomFactor + zoomFactor * factor;
	    				if(zf < zoomMin)
	    					zf = zoomMin;
	    				else if(zf > zoomMax)
	    					zf = zoomMax;
	    				Log.i("test", String.format("current factor %f, zf %f"
		    					, zoomFactor, zf));
	    				if(Math.abs(zf - zoomFactor) > zoomSpec){
	    					zoomFactor = zf;
	    					updateListView();
	    				}
	    			}
	    		}
    			break;
    		default:
    			Log.i("test", String.format("other zoom event %d", event.getActionMasked()));
    			break;
    		}
    		return true;
    	}else{
    		return gestureDetector.onTouchEvent(event);
    	}  	
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        this.elems.clear();
        for (int i = 0; i < 4; i++) {
            div[i] = null;
        }
        data = null;
    }

	protected int getvSpace() {
		return (int) (vSpace * zoomFactor);
	}

	protected int gethSpace() {
		return (int) (hSpace * zoomFactor);
	}

	protected int getItemHeight() {
		return (int) (itemHeight * zoomFactor);
	}

	protected int getItemWidth() {
		return (int) (itemWidth * zoomFactor);
	}

	public int getFontSize() {
		return (int) (fontSize * zoomFactor);
	}
	
	protected int getHead() {
		return (int) (head * zoomFactor);
	}
	
	protected int getImageHeight(){
		return (int) (mImg.getHeight() * zoomFactor);
	}
	
	private double calDistanceBetweenFingers(MotionEvent event) {
		float disX = Math.abs(event.getX(0) - event.getX(1));
		float disY = Math.abs(event.getY(0) - event.getY(1));
		Log.i("test", String.format("x1 %f, y1 %f, x2 %f, y2 %f"
				, event.getX(0), event.getY(0), event.getX(1), event.getY(1)));
		return Math.sqrt(disX * disX + disY * disY);
	}
}
